Passed
Push — master ( d3c785...f638dd )
by Rafael S.
01:33
created

index.js ➔ encode   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
c 1
b 0
f 0
nc 3
dl 0
loc 12
rs 8.8571
nop 1
1
/*!
2
 * imaadpcm
3
 * JavaScript IMA ADPCM codec.
4
 * Copyright (c) 2018 Rafael da Silva Rocha.
5
 * https://github.com/rochars/imaadpcm
6
 *
7
 * References:
8
 * http://www.cs.columbia.edu/~hgs/audio/dvi/
9
 * https://github.com/acida/pyima
10
 * https://wiki.multimedia.cx/index.php/IMA_ADPCM
11
 * 
12
 */
13
14
const byteData = require("byte-data");
15
const int16 = byteData.int16;
16
17
var indexTable = [
18
    -1, -1, -1, -1, 2, 4, 6, 8,
19
    -1, -1, -1, -1, 2, 4, 6, 8];
20
21
var stepTable = [
22
    7, 8, 9, 10, 11, 12, 13, 14,
23
    16, 17, 19, 21, 23, 25, 28, 31,
24
    34, 37, 41, 45, 50, 55, 60, 66,
25
    73, 80, 88, 97, 107, 118, 130, 143,
26
    157, 173, 190, 209, 230, 253, 279, 307,
27
    337, 371, 408, 449, 494, 544, 598, 658,
28
    724, 796, 876, 963, 1060, 1166, 1282, 1411,
29
    1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
30
    3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
31
    7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
32
    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
33
    32767];
34
35
var encoderPredicted = 0;
36
var encoderIndex = 0;
37
var encoderStep = 7;
38
var decoderPredicted = 0;
39
var decoderIndex = 0;
40
var decoderStep = 7;
41
42
/**
43
 * Compress a 16-bit PCM sample into a 4-bit ADPCM sample.
44
 * @param {number} sample The sample.
45
 * @return {number}
46
 */
47
function encodeSample(sample) {
48
    let delta = sample - encoderPredicted;
49
    let value = 0;
50
    if (delta >= 0) {
51
        value = 0;
52
    }
53
    else {
54
        value = 8;
55
        delta = -delta;
56
    }
57
    let step = stepTable[encoderIndex];
58
    let diff = step >> 3;
59
    if (delta > step) {
60
        value |= 4;
61
        delta -= step;
62
        diff += step;
63
    }
64
    step >>= 1;
65
    if (delta > step) {
66
        value |= 2;
67
        delta -= step;
68
        diff += step;
69
    }
70
    step >>= 1;
71
    if (delta > step) {
72
        value |= 1;
73
        diff += step;
74
    }
75
    if (value & 8) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
76
        encoderPredicted -= diff;
77
    }
78
    else {
79
        encoderPredicted += diff;
80
    }
81
    if (encoderPredicted < -0x8000) {
82
        encoderPredicted = -0x8000;
83
    } else if (encoderPredicted > 0x7fff) {
84
        encoderPredicted = 0x7fff;
85
    }
86
    encoderIndex += indexTable[value & 7];
87
    if (encoderIndex < 0) {
88
        encoderIndex = 0;
89
    } else if (encoderIndex > 88) {
90
        encoderIndex = 88;
91
    }
92
    return value;
93
}
94
95
/**
96
 * Decode a 4-bit ADPCM sample into a 16-bit PCM sample.
97
 * @param {number} nibble A 4-bit adpcm sample.
98
 * @return {number}
99
 */
100
function decodeSample(nibble) {
101
    let difference = 0;
102
    if (nibble & 4) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
103
        difference += decoderStep;
104
    }
105
    if (nibble & 2) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
106
        difference += decoderStep >> 1;
107
    }
108
    if (nibble & 1) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
109
        difference += decoderStep >> 2;
110
    }
111
    difference += decoderStep >> 3;
112
    if (nibble & 8) {
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
113
        difference = -difference;
114
    }
115
    decoderPredicted += difference;
116
    if (decoderPredicted > 32767) {
117
        decoderPredicted = 32767;
118
    } else if (decoderPredicted < -32767) {
119
        decoderPredicted = -32767;
120
    }
121
    decoderIndex += indexTable[nibble];
122
    if (decoderIndex < 0) {
123
        decoderIndex = 0;
124
    } else if (decoderIndex > 88) {
125
        decoderIndex = 88;
126
    }
127
    decoderStep = stepTable[decoderIndex];
128
    return decoderPredicted;
129
}
130
131
/**
132
 * Return the head of a ADPCM sample block.
133
 * @param {number} sample The first sample of the block.
134
 * @return {!Array<number>}
135
 */
136
function blockHead(sample) {
137
    encodeSample(sample);
138
    let adpcmSamples = [];
139
    adpcmSamples.push(byteData.pack(sample, int16)[0]);
140
    adpcmSamples.push(byteData.pack(sample, int16)[1]);
141
    adpcmSamples.push(encoderIndex);
142
    adpcmSamples.push(0);
143
    return adpcmSamples;
144
}
145
146
/**
147
 * Encode a block of 505 16-bit samples as 4-bit ADPCM samples.
148
 * @param {!Array<number>} block A sample block of 505 samples.
149
 * @return {!Array<number>}
150
 */
151
function encodeBlock(block) {
152
    let adpcmSamples = blockHead(block[0]);
153
    for (let i=3; i<block.length; i+=2) {
154
        let sample2 = encodeSample(block[i]);
155
        let sample = encodeSample(block[i + 1]);
156
        adpcmSamples.push((sample << 4) | sample2);
157
    }
158
    while (adpcmSamples.length < 256) {
159
        adpcmSamples.push(0);
160
    }
161
    return adpcmSamples;
162
}
163
164
/**
165
 * Decode a block of 256 ADPCM samples into 16-bit PCM samples.
166
 * @param {!Array<number>} block A adpcm sample block of 256 samples.
167
 * @return {!Array<number>}
168
 */
169
function decodeBlock(block) {
170
    decoderPredicted = byteData.unpack([block[0], block[1]], int16);
171
    decoderIndex = block[2];
172
    decoderStep = stepTable[decoderIndex];
173
    let result = [
174
            decoderPredicted,
175
            byteData.unpack([block[2], block[3]], int16)
176
        ];
177
    for (let i=4; i<block.length; i++) {
178
        let original_sample = block[i];
179
        let second_sample = original_sample >> 4;
180
        let first_sample = (second_sample << 4) ^ original_sample;
181
        result.push(decodeSample(first_sample));
182
        result.push(decodeSample(second_sample));
183
    }
184
    return result;
185
}
186
187
/**
188
 * Encode 16-bit PCM samples into 4-bit IMA ADPCM samples.
189
 * @param {!Array<number>} samples A array of samples.
190
 * @return {!Array<number>}
191
 */
192
function encode(samples) {
193
    let adpcmSamples = [];
194
    let block = [];
195
    for (let i=0; i<samples.length; i++) {
196
        block.push(samples[i]);
197
        if ((i % 505 == 0 && i != 0) || i == samples.length - 1) {
198
            adpcmSamples = adpcmSamples.concat(encodeBlock(block));
199
            block = [];
200
        }
201
    }
202
    return adpcmSamples;
203
}
204
205
/**
206
 * Decode IMA ADPCM samples into 16-bit PCM samples.
207
 * @param {!Array<number>} adpcmSamples A array of ADPCM samples.
208
 * @param {number} blockAlign The block size.
209
 * @return {!Array<number>}
210
 */
211
function decode(adpcmSamples, blockAlign=256) {
212
    let samples = [];
213
    let block = [];
214
    for (let i=0; i<adpcmSamples.length; i++) {
215
        if (i % blockAlign == 0 && i != 0) {            
216
            samples = samples.concat(decodeBlock(block));
217
            block = [];
218
        }
219
        block.push(adpcmSamples[i]);
220
    }
221
    return samples;
222
}
223
224
module.exports.encode = encode;
225
module.exports.decode = decode;
226
module.exports.encodeBlock = encodeBlock;
227
module.exports.decodeBlock = decodeBlock;
228